package dao

import (
	"context"
	"database/sql"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"prod/api/prod"
	"prod/internal/constant"
	"prod/internal/model"
	"prod/pkg/ecode"
)

func (d *Dao) GetProdCountByTypeId(ctx context.Context, sgId, pStoreId, typeId uint32) (prodCount int64, err error) {
	if err = d.TransContext(ctx).Model(&model.Product{}).Where(
		"sg_id = ? AND p_store_id = ? AND type_id = ? AND status = ?",
		sgId, pStoreId, typeId, true).Count(&prodCount).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) getQueryTypeIds(ctx context.Context, typeId, sgId, pStoreId uint32) (
	typeIds []uint32, err error) {
	var prodType *model.ProdType
	if prodType, err = d.GetProdTypeByID(ctx, typeId, sgId, pStoreId); err != nil {
		if errors.Cause(err) == gorm.ErrRecordNotFound {
			err = ecode.AppendMessage(constant.ErrObjectNotExist, "商品类别")
		}
		return
	}
	if prodType.ParentId != 0 {
		// 二级分类
		typeIds = []uint32{prodType.ID}
	} else {
		// 一级分类，需要获取全部子类
		var subProdTypes []*model.ProdType
		if subProdTypes, err = d.GetSubTypes(ctx, sgId, pStoreId, typeId); err != nil {
			return
		}
		if len(subProdTypes) == 0 {
			// 没有子类则为空列表
			return
		}
		typeIds = make([]uint32, len(subProdTypes))
		for i, subProdType := range subProdTypes {
			typeIds[i] = subProdType.ID
		}
	}
	return
}

func (d *Dao) LoadProdListByPage(ctx context.Context, sgId, pStoreId, typeId, pageNo, pageSize uint32,
	nature prod.Nature, query string, excludedProdIds []uint64, excludedMenuID uint32) (
	prods []*prod.SimpleProdView, total uint32, err error) {
	db := d.TransContext(ctx).Model(&model.Product{}).Select(
		"id, name, type_id as prod_type_id, title_pics[0] as title_pic").Where(
		"sg_id = ? AND p_store_id = ? AND status = ?",
		sgId, pStoreId, true)
	if nature != prod.Nature_NATURE_ALL {
		db = db.Where("nature = ? ", int(nature))
	}
	if typeId != 0 {
		var queryTypeIds []uint32
		if queryTypeIds, err = d.getQueryTypeIds(ctx, typeId, sgId, pStoreId); err != nil {
			return
		}
		if len(queryTypeIds) != 0 {
			db = db.Where("type_id in ?", queryTypeIds)
		} else {
			return
		}
	}
	if query != "" {
		db = db.Where("name like ?", "%"+query+"%")
	}
	if len(excludedProdIds) != 0 {
		db = db.Where("id not in ?", excludedProdIds)
	}
	if excludedMenuID != 0 {
		db = db.Where("id not in (SELECT prod_id FROM pd_menu_prod "+
			"WHERE menu_id = ? AND status = ?)",
			excludedMenuID, true)
	}
	var rawTotal int64
	if err = db.Count(&rawTotal).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	total = uint32(rawTotal)
	if err = db.Order("id asc").Offset(int((pageNo - 1) * pageSize)).Limit(
		int(pageSize)).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetProdSkusByProdIds(ctx context.Context, ids []uint64, sgId uint32) (
	skus []*model.Sku, err error) {
	if err = d.TransContext(ctx).Model(&model.Sku{}).Where(
		"prod_id in ? AND sg_id = ? AND status = ?",
		ids, sgId, true).Order("prod_id, price asc").Scan(&skus).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) GetProdSkusBySkuIds(ctx context.Context, ids []uint64, sgId uint32) (
	skus []*model.Sku, err error) {
	if err = d.TransContext(ctx).Model(&model.Sku{}).Where(
		"id in ? AND sg_id = ? AND status = ?",
		ids, sgId, true).Scan(&skus).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) GetAllProdSkusByProdIds(ctx context.Context, ids []uint64) (skus []*model.Sku, err error) {
	if err = d.TransContext(ctx).Model(&model.Sku{}).Where(
		"prod_id in ?",
		ids, true).Order("prod_id, price asc").Scan(&skus).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) GetProdInfo(ctx context.Context, id uint64, sgId, pStoreId uint32) (
	prod *model.Product, err error) {
	prod = &model.Product{}
	if err = d.TransContext(ctx).Model(&model.Product{}).Where(
		"id = ? AND sg_id = ? AND p_store_id = ? AND status = ?",
		id, sgId, pStoreId, true).Take(prod).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) GetProdInfoWithStatus(ctx context.Context, id uint64, sgId uint32) (
	prod *model.Product, err error) {
	prod = &model.Product{}
	if err = d.TransContext(ctx).Model(&model.Product{}).Where(
		"id = ? AND sg_id = ?", id, sgId).Take(prod).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) GetProdInfoByName(ctx context.Context, name string, sgId, pStoreId uint32) (
	prod *model.Product, err error) {
	prod = &model.Product{}
	if err = d.TransContext(ctx).Model(&model.Product{}).Where(
		"sg_id = ? AND p_store_id = ? AND name = ? AND status = ?",
		sgId, pStoreId, name, true).Take(prod).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) LockProdByIDs(ctx context.Context, ids []uint64, sgId, pStoreId uint32) (
	prods []*model.Product, err error) {
	if err = d.TransContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Model(
		&model.Product{}).Where(
		"id in ? AND sg_id=? AND p_store_id=? AND status=?",
		ids, sgId, pStoreId, true).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetProdByIDs(ctx context.Context, ids []uint64, sgId, pStoreId uint32) (
	prods []*model.Product, err error) {
	if err = d.TransContext(ctx).Model(
		&model.Product{}).Where(
		"id in ? AND sg_id=? AND p_store_id=? AND status=?",
		ids, sgId, pStoreId, true).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) DeleteProdByIDs(ctx context.Context, ids []uint64, operator uint32) (err error) {
	if err = d.TransContext(ctx).Model(&model.Product{}).Where(
		"id in ? AND status=?", ids, true).Updates(map[string]interface{}{
		"status":   sql.NullBool{},
		"operator": operator,
	}).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) DeleteSkuByIDs(ctx context.Context, ids []uint64, prodId uint64, operator uint32) (err error) {
	if err = d.TransContext(ctx).Model(&model.Sku{}).Where(
		"id in ? AND status=?", ids, true).Updates(map[string]interface{}{
		"status":   false,
		"operator": operator,
	}).Error; err != nil {
		err = d.processSqlError(err)
	}
	if err = d.TransContext(ctx).Model(&model.MenuSku{}).Where(
		"prod_id = ? AND sku_id in ? AND status=?", prodId, ids, true).Updates(map[string]interface{}{
		"status":   false,
		"operator": operator,
	}).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetSelectProdsByIDs(ctx context.Context, ids []uint64, sgId uint32) (
	prods []*model.ProdQueryModel, err error) {
	err = d.processSqlError(d.TransContext(ctx).Table("pd_product AS p1").Select(
		"p1.id, p1.name, p1.p_store_id, p1.type_id as prod_type_id, "+
			"CONCAT(p3.name, '/', p2.name) as prod_type_name, p1.phonetic, p1.unit, "+
			"p1.nature, COALESCE(p1.title_pics[1], '') as pic, p1.group_list as groups, "+
			"p1.group_detail as group_json").Joins(
		"JOIN pd_prod_type as p2 ON p1.type_id = p2.id").Joins(
		"JOIN pd_prod_type as p3 oN p2.parent_id=p3.id").Where(
		"p1.id in ? AND p1.sg_id = ? AND p1.status = ?",
		ids, sgId, true).Scan(&prods).Error)
	return
}

func (d *Dao) FindStoreCustomPrice(ctx context.Context, prodIds []uint64) (
	skus []*prod.SelectionProdsFullResponse_StoreCustomPrice, err error) {
	err = d.processSqlError(d.TransContext(ctx).Model(&model.MenuSku{}).Joins(
		"JOIN pd_menu_store ON pd_menu_sku.menu_id=pd_menu_store.menu_id").Where(
		"prod_id in ?", prodIds).Scan(&skus).Error)
	return
}
